//
//  BELManagement.m
//  JiuLe
//
//  Created by 黄鑫 on 2016/11/3.
//  Copyright © 2016年 Fine. All rights reserved.
//

#import "BELManagement.h"
#import "BELCommand.h"

static BELManagement *_management;


@implementation BELManagement

+ (instancetype)allocWithZone:(struct _NSZone *)zone{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _management = [super allocWithZone:zone];
    });
    return _management;
}

+ (instancetype)sharedInstance{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _management = [[self alloc] initPrivate];
    });
    return _management;
}

- (instancetype)init {
    @throw [NSException exceptionWithName:@"这个是个单例"
                                   reason:@"应该这样调用 [BELManagement sharedInstance]"
                                 userInfo:nil];
    return nil;
}

- (instancetype)initPrivate {
    self  = [super init];
    return self;
}

- (id)copyWithZone:(NSZone *)zone{
    return _management;
}

//1：创建中心设备
- (void)creatCentralManagement{
    //蓝牙架构：ARM
    /*
     http://www.tuicool.com/articles/n22ea2V
    参数说明
    delegate：   接受中心事件
    queue：      指明在哪个队列处理事件，为nil时表示在主线程处理
    options：    有两个选择
                        NSString *const CBCentralManagerOptionShowPowerAlertKey;用该参量初始化中心设备时，当蓝牙开关未打开时会弹出警告框。
                        使用：非后台模式CBCentralManagerOptionShowPowerAlertKey:[NSNumber numberWithBool:NO]
                        NSString *const CBCentralManagerOptionRestoreIdentifierKey;该参表示添加状态存储和恢复支持
     */
    self.cManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:@{CBCentralManagerOptionRestoreIdentifierKey:@"BLESericeBrowser"}];
}

//3：搜索外围设备
- (void)searchPeripheral:(int)timeout{
    /*开始搜索外围设备 
     services:搜索的设备 @[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]]
     options：扫描参数：http://www.jianshu.com/p/839a2044f944*/
    [self.cManager scanForPeripheralsWithServices:nil options:0];
}

//5：连接外围设备
- (void) connectPeripheral:(CBPeripheral *)peripheral status:(BOOL)status{
    if(status == TRUE){
        printf("Connecting to peripheral with UUID : %s\r\n",[[[peripheral identifier] UUIDString] UTF8String]);
        self.cPeripheral = peripheral;
        self.cPeripheral.delegate = self;
        [self.cManager connectPeripheral:self.cPeripheral options:nil];//开始连接外围设备
    }else{
        self.cPeripheral = peripheral;
        self.cPeripheral.delegate = self;
        [self.cManager cancelPeripheralConnection:self.cPeripheral];//取消连接外围设备
    }
}

#pragma mark - CBCentralManagerDelegate
- (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary<NSString *,id> *)dict{
    self.peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey];
}

//2：回调显示中心设备状态
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
    printf("Status of CoreBluetooth central manager changed %d (%s)\r\n",(int)central.state,[self centralManagerStateToString:central.state]);
    [self.delegate backBluetoothStatus];
}

//4：搜索外围设备回调
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI{
    for(int i = 0; i < self.peripherals.count; i++) {
        CBPeripheral *p = [self.peripherals objectAtIndex:i];
        int nResult = [[p identifier] isEqual:([peripheral identifier] )];
        if (nResult == 1) {
            [self.peripherals replaceObjectAtIndex:i withObject:peripheral];
            printf("Duplicate UUID found updating ...\r\n");
            return;
        }else if(nResult == -1){// Work around: if the UUIDs don't match, check the advertisement data
            if(![p.name isEqualToString:peripheral.name]){
                [self.peripherals replaceObjectAtIndex:i withObject:peripheral];
                printf("Duplicate UUID found updating ...\r\n");
                return;
            }
        }
    }
    if ([[advertisementData objectForKey:@"kCBAdvDataLocalName"] isEqualToString:@"JL19073"]) {//
        [self connectPeripheral:peripheral status:YES];
        [self.peripherals addObject:peripheral];
    }
    NSLog(@"New UUID, adding  %@ ",[advertisementData objectForKey:@"kCBAdvDataLocalName"]);
    printf("didDiscoverPeripheral\r\n");
}

//6：连接外围设备成功回调
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
    NSLog(@"外围设备连接成功 UUID：%s",[[[peripheral identifier] UUIDString] UTF8String]);
     self.cPeripheral = peripheral;
    self.bluetoothStatus = BluetoothStatusConnection;
    [self.delegate backBluetoothStatus];
    [self stopSearch];
    //7：搜索外围设备提供的服务
    [self.cPeripheral discoverServices:nil];
}

//6：连接外围设备失败回调
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
    NSLog(@"外围设备连接失败 UUID：%s",[[[peripheral identifier] UUIDString] UTF8String]);
    self.bluetoothStatus = BluetoothStatusConnectionFailed;
    [self.delegate backBluetoothStatus];
    [self.cManager stopScan];

}

#pragma mark - CBPeripheralDelegate
//8：已发现服务后的回调
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
    for (CBService *service in peripheral.services) {
        NSLog(@"service name:%@ service UUID:%@", [self getServiceName:service.UUID],[service.UUID.data description]);
        //9:搜索服务的特征
        [peripheral discoverCharacteristics:nil forService:service];
    }
}

//10 已搜索到服务特征回调
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
    for (CBCharacteristic *characteristic in service.characteristics) {
        NSLog(@"Discovered characteristic %@", characteristic);
        //读取特征的值
        //[peripheral readValueForCharacteristic:characteristic];
        //订阅特征的值
        //[peripheral setNotifyValue:YES forCharacteristic:characteristic];
        //写值到特征中（就是发数据给外围设备）
        if ([characteristic.UUID.UUIDString isEqualToString:@"0C366E80-CF3A-11E1-9AB4-0002A5D5C51B"]) {
            //Byte   CMD_HEAD = 0x01;
            //Byte   CMD_LENGHT = 0x0B;
            //Byte   CMD_SORT = 0x05;
            //Byte byte4[] = {CMD_HEAD,CMD_LENGHT,CMD_SORT};
            //NSData *data25 = [NSData dataWithBytes:byte4 length:sizeof(byte4)];
            //[[BELCommand alloc] setMovement:@"15" ccid:@"19073"]
            //CBCharacteristicWriteWithoutResponse 与硬件提供的服务特征必须一致 不一致会导致发送数据失败，提示未知错误
            [peripheral writeValue:[[BELCommand alloc] setMovement:@"15" ccid:@"19073"] forCharacteristic:characteristic type:CBCharacteristicWriteWithoutResponse];
        }
    }
}

//11：已读取、订阅到特征值的回调
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    NSData *data = characteristic.value;
    NSString* newStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"已读取到的数据 %@",newStr);
}

//12：已写数据到特征值的回调
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    if (error) {
        NSLog(@"Did write characteristic value : %@ with ID %@", characteristic.value, characteristic.UUID);
        NSLog(@"With error: %@", [error localizedDescription]);
    }
}

//设置搜索时间
- (void)scanTimer:(NSTimer *)timer {
}

- (void)stopSearch{
    [self.cManager stopScan];
}

//显示中心设备状态
- (const char *)centralManagerStateToString:(int)state{
    switch(state) {
        case CBCentralManagerStateUnknown:
            self.bluetoothStatus = BluetoothStatusConnectionFailed;
            return "未知";
        case CBCentralManagerStateResetting:
            self.bluetoothStatus = BluetoothStatusConnectionFailed;
            return "复位";
        case CBCentralManagerStateUnsupported:
            self.bluetoothStatus = BluetoothStatusConnectionFailed;
            return "表明设备不支持蓝牙低功耗 ";
        case CBCentralManagerStateUnauthorized:
            return "该应用程序是无权使用蓝牙低功耗";
        case CBCentralManagerStatePoweredOff:
            self.bluetoothStatus = BluetoothStatusPhoneNotOpen;
            return "手机蓝牙未打开";
        case CBCentralManagerStatePoweredOn:
            self.bluetoothStatus = BluetoothStatusPhoneOpen;
            return "手机蓝牙已打开";
        default:
            self.bluetoothStatus = BluetoothStatusConnectionFailed;
            return "State unknown";
    }
    return "Unknown state";
}

- (NSMutableArray *)peripherals{
    if (_peripherals == nil) {
        _peripherals = [NSMutableArray array];
    }
    return _peripherals;
}

- (BOOL)checkBluetoothStatus{

    return YES;
}

- (UInt16)cBUUIDToInt:(CBUUID *) UUID {
    char b1[16];
    [UUID.data getBytes:b1];
    return ((b1[0] << 8) | b1[1]);
}

- (NSString *)getServiceName:(CBUUID *)UUID{
    
    UInt16 _uuid = [self cBUUIDToInt:UUID];
    
    switch(_uuid)
    {
        case 0x1800: return @"Generic Access"; break;
        case 0x1801: return @"Generic Attribute"; break;
        case 0x1802: return @"Immediate Alert"; break;
        case 0x1803: return @"Link Loss"; break;
        case 0x1804: return @"Tx Power"; break;
        case 0x1805: return @"Current Time Service"; break;
        case 0x1806: return @"Reference Time Update Service"; break;
        case 0x1807: return @"Next DST Change Service"; break;
        case 0x1808: return @"Glucose"; break;
        case 0x1809: return @"Health Thermometer"; break;
        case 0x180A: return @"Device Information"; break;
        case 0x180B: return @"Network Availability Service"; break;
        case 0x180C: return @"Watchdog"; break;
        case 0x180D: return @"Heart Rate"; break;
        case 0x180E: return @"Phone Alert Status Service"; break;
        case 0x180F: return @"Battery Service"; break;
        case 0x1810: return @"Blood Pressure"; break;
        case 0x1811: return @"Alert Notification Service"; break;
        case 0x1812: return @"Human Interface Device"; break;
        case 0x1813: return @"Scan Parameters"; break;
        case 0x1814: return @"RUNNING SPEED AND CADENCE"; break;
        case 0x1815: return @"Automation IO"; break;
        case 0x1816: return @"Cycling Speed and Cadence"; break;
        case 0x1817: return @"Pulse Oximeter"; break;
        case 0x1818: return @"Cycling Power Service"; break;
        case 0x1819: return @"Location and Navigation Service"; break;
        case 0x181A: return @"Continous Glucose Measurement Service"; break;
        case 0x2A00: return @"Device Name"; break;
        case 0x2A01: return @"Appearance"; break;
        case 0x2A02: return @"Peripheral Privacy Flag"; break;
        case 0x2A03: return @"Reconnection Address"; break;
        case 0x2A04: return @"Peripheral Preferred Connection Parameters"; break;
        case 0x2A05: return @"Service Changed"; break;
        case 0x2A06: return @"Alert Level"; break;
        case 0x2A07: return @"Tx Power Level"; break;
        case 0x2A08: return @"Date Time"; break;
        case 0x2A09: return @"Day of Week"; break;
        case 0x2A0A: return @"Day Date Time"; break;
        case 0x2A0B: return @"Exact Time 100"; break;
        case 0x2A0C: return @"Exact Time 256"; break;
        case 0x2A0D: return @"DST Offset"; break;
        case 0x2A0E: return @"Time Zone"; break;
        case 0x2A0F: return @"Local Time Information"; break;
        case 0x2A10: return @"Secondary Time Zone"; break;
        case 0x2A11: return @"Time with DST"; break;
        case 0x2A12: return @"Time Accuracy"; break;
        case 0x2A13: return @"Time Source"; break;
        case 0x2A14: return @"Reference Time Information"; break;
        case 0x2A15: return @"Time Broadcast"; break;
        case 0x2A16: return @"Time Update Control Point"; break;
        case 0x2A17: return @"Time Update State"; break;
        case 0x2A18: return @"Glucose Measurement"; break;
        case 0x2A19: return @"Battery Level"; break;
        case 0x2A1A: return @"Battery Power State"; break;
        case 0x2A1B: return @"Battery Level State"; break;
        case 0x2A1C: return @"Temperature Measurement"; break;
        case 0x2A1D: return @"Temperature Type"; break;
        case 0x2A1E: return @"Intermediate Temperature"; break;
        case 0x2A1F: return @"Temperature in Celsius"; break;
        case 0x2A20: return @"Temperature in Fahrenheit"; break;
        case 0x2A21: return @"Measurement Interval"; break;
        case 0x2A22: return @"Boot Keyboard Input Report"; break;
        case 0x2A23: return @"System ID"; break;
        case 0x2A24: return @"Model Number String"; break;
        case 0x2A25: return @"Serial Number String"; break;
        case 0x2A26: return @"Firmware Revision String"; break;
        case 0x2A27: return @"Hardware Revision String"; break;
        case 0x2A28: return @"Software Revision String"; break;
        case 0x2A29: return @"Manufacturer Name String"; break;
        case 0x2A2A: return @"IEEE 11073-20601 Regulatory Certification Data List"; break;
        case 0x2A2B: return @"Current Time"; break;
        case 0x2A2C: return @"Elevation"; break;
        case 0x2A2D: return @"Latitude"; break;
        case 0x2A2E: return @"Longitude"; break;
        case 0x2A2F: return @"Position 2D"; break;
        case 0x2A30: return @"Position 3D"; break;
        case 0x2A31: return @"Scan Refresh"; break;
        case 0x2A32: return @"Boot Keyboard Output Report"; break;
        case 0x2A33: return @"Boot Mouse Input Report"; break;
        case 0x2A34: return @"Glucose Measurement Context"; break;
        case 0x2A35: return @"Blood Pressure Measurement"; break;
        case 0x2A36: return @"Intermediate Cuff Pressure"; break;
        case 0x2A37: return @"Heart Rate Measurement"; break;
        case 0x2A38: return @"Body Sensor Location"; break;
        case 0x2A39: return @"Heart Rate Control Point"; break;
        case 0x2A3A: return @"Removable"; break;
        case 0x2A3B: return @"Service Required"; break;
        case 0x2A3C: return @"Scientific Temperature in Celsius"; break;
        case 0x2A3D: return @"String"; break;
        case 0x2A3E: return @"Network Availability"; break;
        case 0x2A3F: return @"Alert Status"; break;
        case 0x2A40: return @"Ringer Control Point"; break;
        case 0x2A41: return @"Ringer Setting"; break;
        case 0x2A42: return @"Alert Category ID Bit Mask"; break;
        case 0x2A43: return @"Alert Category ID"; break;
        case 0x2A44: return @"Alert Notification Control Point"; break;
        case 0x2A45: return @"Unread Alert Status"; break;
        case 0x2A46: return @"New Alert"; break;
        case 0x2A47: return @"Supported New Alert Category"; break;
        case 0x2A48: return @"Supported Unread Alert Category"; break;
        case 0x2A49: return @"Blood Pressure Feature"; break;
        case 0x2A4A: return @"HID Information"; break;
        case 0x2A4B: return @"Report Map"; break;
        case 0x2A4C: return @"HID Control Point"; break;
        case 0x2A4D: return @"Report"; break;
        case 0x2A4E: return @"Protocol Mode"; break;
        case 0x2A4F: return @"Scan Interval Window"; break;
        case 0x2A50: return @"PnP ID"; break;
        case 0x2A51: return @"Glucose Features"; break;
        case 0x2A52: return @"Record Access Control Point"; break;
        case 0x2A53: return @"RSC Measurement"; break;
        case 0x2A54: return @"RSC Feature"; break;
        case 0x2A55: return @"SC Control Point"; break;
        case 0x2A56: return @"Digital Input"; break;
        case 0x2A57: return @"Digital Output"; break;
        case 0x2A58: return @"Analog Input"; break;
        case 0x2A59: return @"Analog Output"; break;
        case 0x2A5A: return @"Aggregate Input"; break;
        case 0x2A5B: return @"CSC Measurement"; break;
        case 0x2A5C: return @"CSC Feature"; break;
        case 0x2A5D: return @"Sensor Location"; break;
        case 0x2A5E: return @"Pulse Oximetry Spot-check Measurement"; break;
        case 0x2A5F: return @"Pulse Oximetry Continuous Measurement"; break;
        case 0x2A60: return @"Pulse Oximetry Pulsatile Event"; break;
        case 0x2A61: return @"Pulse Oximetry Features"; break;
        case 0x2A62: return @"Pulse Oximetry Control Point"; break;
        case 0x2A63: return @"Cycling Power Measurement Characteristic"; break;
        case 0x2A64: return @"Cycling Power Vector Characteristic"; break;
        case 0x2A65: return @"Cycling Power Feature Characteristic"; break;
        case 0x2A66: return @"Cycling Power Control Point Characteristic"; break;
        case 0x2A67: return @"Location and Speed Characteristic"; break;
        case 0x2A68: return @"Navigation Characteristic"; break;
        case 0x2A69: return @"Position Quality Characteristic"; break;
        case 0x2A6A: return @"LN Feature Characteristic"; break;
        case 0x2A6B: return @"LN Control Point Characteristic"; break;
        case 0x2A6C: return @"CGM Measurement Characteristic"; break;
        case 0x2A6D: return @"CGM Features Characteristic"; break;
        case 0x2A6E: return @"CGM Status Characteristic"; break;
        case 0x2A6F: return @"CGM Session Start Time Characteristic"; break;
        case 0x2A70: return @"Application Security Point Characteristic"; break;
        case 0x2A71: return @"CGM Specific Ops Control Point Characteristic"; break;
        default:
            return @"Custom Profile";
            break;
    }
}


@end
